<?php
namespace Wechat\Controller\Component;

use Cake\Controller\Component;
use Cake\ORM\TableRegistry;
use Psr\Log\LogLevel;
use Cake\Cache\Cache;
use Payment\Controller\Component\OmnipayComponent;
use Omnipay\Omnipay;
use Omnipay\WechatPay\Helper;
use Cake\Routing\Router;
use Cake\Network\Exception\NotFoundException;

/**
 * Wechat component
 *
 * @property \User\Controller\Component\UsersComponent $Users
 */
class WechatComponent extends Component
{

    public $components = [
        "User.Users"
    ];

    public $id = 10000;

    var $appId = "";

    var $mchId = 1275963501;

    var $apiKey = "";

    var $appSecret = "";

    var $host = "www.mofing.com";

    var $token = "";

    var $aesKey = "";

    /**
     * Default configuration.
     *
     * @var array
     */
    protected $_defaultConfig = [];

    private $wxConfig;

    private $application;

    // 初始化
    public function initialize(array $config)
    {
        parent::initialize($config);
        if (! empty($config["id"])) {
            $this->id = $config["id"];
        }
        $this->getWechatConfigs($this->id);
    }

    /**
     * 初始化配置
     */
    private function getWechatConfigs($id)
    {
        $Wechats = TableRegistry::get("Wechat.Wechats");
        // 这里要缓存一年，你信吗？
        $config = $Wechats->get($id, [
            "cache" => "_cake_core_"
        ]);
        $this->setConfig("appId", $config->appId);
        $this->setConfig("appSecret", $config->appSecret);
        $this->setConfig("token", $config->token);
        $this->setConfig("aesKey", $config->aesKey);
        $this->setConfig("apiKey", $config->apiKey);
        $this->setConfig("mchId", $config->mchId);
        $this->appId = $config->appId;
        $this->appSecret = $config->secret;
        $this->token = $config->token;
        $this->aesKey = $config->aesKey;
        $this->apiKey = $config->apiKey;
        $this->mchId = $config->mchId;
    }

    /**
     * 网页/公众号支付
     *
     * {@inheritdoc}
     *
     * @see \Payment\Controller\Component\OmnipayComponent::getJSAPI()
     * @link https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
     */
    public function getJSAPI($data)
    {
        /**
         *
         * @var \Omnipay\WechatPay\BaseAbstractGateway $gateway
         */
        $gateway = Omnipay::create('WechatPay_Js');
        $gateway->setAppId($this->appId);
        $gateway->setMchId($this->mchId);
        $gateway->setApiKey($this->apiKey);
        $gateway->setNotifyUrl($this->getNotifyUrl());
        $order = [
            'body' => $data["subject"],
            'out_trade_no' => $data["id"],
            'openid' => $data["buyerid"],
            'total_fee' => intval($data["total"] * 100), // =0.01
            'spbill_create_ip' => $data["ip"],
            'time_start' => date("YmdHis"),
            'time_expire' => date("YmdHis", time() + 600),
            'goods_tag' => "",
            'fee_type' => 'CNY'
        ];
        $request = $gateway->purchase($order);
        $this->log($request->getParameters(), LogLevel::INFO);
        $this->log($request->getData(), LogLevel::INFO);
        $response = $request->send();
        $content = "payFail();";
        if ($response->isSuccessful()) {
            $this->log($response->getJsOrderData(), LogLevel::INFO);
            $data = $response->getJsOrderData();
            $content = 'wx.chooseWXPay({timestamp: ' . $data["timeStamp"] . ',nonceStr: "' . $data["nonceStr"] . '",package: "' . $data["package"] . '",signType: "' . $data["signType"] . '",paySign: "' . $data["paySign"] . '",
              success: function (res) {paySuccess();}, cancel: function (res){payCancel();return true;},fail: function(res) {payFail();}});';
        } else {
            $this->log($response->getData(), LogLevel::INFO);
        }
        return $content;
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see \Payment\Controller\Component\OmnipayComponent::getApp()
     * @link https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     */
    public function getApp($data)
    {
        $this->log("微信app支付", LogLevel::INFO);
        /**
         *
         * @var \Omnipay\WechatPay\BaseAbstractGateway $gateway
         */
        $gateway = Omnipay::create('WechatPay_App');
        $gateway->setAppId($this->appId);
        $gateway->setMchId($this->mchId);
        $gateway->setApiKey($this->apiKey);
        $gateway->setNotifyUrl($this->getNotifyUrl());
        $order = [
            'body' => $data["subject"],
            'out_trade_no' => $data["id"],
            'total_fee' => intval($data["total"] * 100), // =0.01
            'spbill_create_ip' => $data["ip"],
            'fee_type' => 'CNY'
        ];
        
        /**
         *
         * @var \Omnipay\WechatPay\Message\CreateOrderRequest $request
         * @var \Omnipay\WechatPay\Message\CreateOrderResponse $response
         */
        $request = $gateway->purchase($order);
        $response = $request->send();
        
        // available methods
        if ($response->isSuccessful()) {
            $this->log($response->getData(), LogLevel::INFO);
            // $data = $response->getAppOrderData(); // For WechatPay_App
            // $this->log($data, LogLevel::INFO);
            // 这里只需要返回与支付id就可以了
            return $response->getPrepayId();
        }
        return false;
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see \Payment\Controller\Component\OmnipayComponent::getNative()
     * @link https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
     */
    public function getNative($data)
    {
        // gateways: WechatPay_App, WechatPay_Native, WechatPay_Js, WechatPay_Pos, WechatPay_Mweb
        /**
         *
         * @var \Omnipay\WechatPay\BaseAbstractGateway $gateway
         */
        $gateway = Omnipay::create('WechatPay_Native');
        $gateway->setAppId($this->appId);
        $gateway->setMchId($this->mchId);
        $gateway->setApiKey($this->apiKey);
        $gateway->setNotifyUrl($this->getNotifyUrl());
        
        $order = [
            'body' => $data["subject"],
            'out_trade_no' => $data["id"],
            'total_fee' => intval($data["total"] * 100), // =0.01
            'spbill_create_ip' => $data["ip"],
            'fee_type' => 'CNY'
        ];
        
        /**
         *
         * @var \Omnipay\WechatPay\Message\CreateOrderRequest $request
         * @var \Omnipay\WechatPay\Message\CreateOrderResponse $response
         */
        $request = $gateway->purchase($order);
        $response = $request->send();
        
        if ($response->isSuccessful()) {
            $this->log($response->getData(), LogLevel::INFO);
            // 返回二维码地址
            $url = $response->getCodeUrl(); // For Native Trade Type
            $this->log($url, LogLevel::INFO);
            return $url;
        }
        return "";
    }

    public function checkSignature(&$data)
    {
        $data = $data["xml"];
        /**
         *
         * @var \Omnipay\WechatPay\BaseAbstractGateway $gateway
         */
        $gateway = Omnipay::create('WechatPay');
        $gateway->setAppId($this->appId);
        $gateway->setMchId($this->mchId);
        $gateway->setApiKey($this->apiKey);
        
        $response = $gateway->completePurchase([
            'request_params' => $data
        ])->send();
        
        $this->log($response->getData(), LogLevel::INFO);
        if ($response->isPaid()) {
            // 转换支付金额
            $data["total"] = $data["total_fee"] / 100;
            $this->log("wechat checkSignature success", LogLevel::INFO);
            // pay success
            echo Helper::array2xml([
                "return_code" => "SUCCESS",
                "return_msg" => "ok"
            ]);
            return true;
        } else {
            $this->log("wechat checkSignature success", LogLevel::INFO);
            // pay fail
            echo Helper::array2xml([
                "return_code" => "FAIL",
                "return_msg" => ""
            ]);
            return false;
        }
    }

    public function getAppId()
    {
        return $this->appId;
    }

    public function valid($signature, $timestamp, $nonce)
    {
        $tmpArr = array(
            $this->token,
            $timestamp,
            $nonce
        );
        // use SORT_STRING rule
        sort($tmpArr, SORT_STRING);
        $tmpStr = implode($tmpArr);
        $tmpStr = sha1($tmpStr);
        
        if ($tmpStr == $signature) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 构造授权地址，等待跳转
     */
    public function getWxOauth($type = 1, $redirect_uri = "")
    {
        if ($type == 1) {
            // 不弹出授权页面，直接跳转，只能获取用户openid
            $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $this->appId . "&redirect_uri=" . $redirect_uri . "&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect";
        } else {
            // 弹出授权页面，可通过openid拿到昵称、性别、所在地。并且，即使在未关注的情况下，只要用户授权，也能获取其信息
            $url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" . $this->appId . "&redirect_uri=" . $redirect_uri . "&response_type=code&scope=snsapi_userinfo&state=STATE&connect_redirect=1#wechat_redirect";
        }
        return $url;
    }

    /**
     * 用旧refresh_token刷新token
     */
    public function getWxRefreshToken($data)
    {
        $url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" . $this->appId . "&grant_type=refresh_token&refresh_token=" . $data->refresh_token;
        $res = json_decode($this->httpGet($url), true);
        // {"errcode":40029,"errmsg":"invalid code"}
        if (! empty($res["errcode"])) {
            // 有问题
            $this->log("刷新token有问题");
            return false;
        }
        $this->log("更新令牌保存信息" . $res["openid"], LogLevel::INFO);
        // 保存新的令牌
        $MarketWeixinUsers = TableRegistry::get("User.MarketWeixinUsers");
        $data = $MarketWeixinUsers->patchEntity($data, $res);
        if ($MarketWeixinUsers->save($data)) {
            return true;
        }
        return false;
    }

    public function GetSignPackage($url = "")
    {
        $jsapiTicket = $this->getJsApiTicket();
        if (empty($url)) {
            $protocol = (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
            $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        }
        $timestamp = time();
        $nonceStr = $this->createNonceStr();
        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
        $signature = sha1($string);
        $signPackage = [
            "appId" => $this->appId,
            "nonceStr" => $nonceStr,
            "timestamp" => $timestamp,
            "signature" => $signature
        ];
        return $signPackage;
    }

    private function getJsApiTicket()
    {
        $key = $this->appId . "_jsapi_ticket_2";
        $ticket = Cache::read($key, $this->cache_data_config);
        if (empty($ticket)) {
            $accessToken = $this->getAccessToken();
            if (empty($accessToken)) {
                return "";
            }
            // 如果是企业号用以下 URL 获取 ticket
            // $url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=$accessToken";
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accessToken";
            $res = json_decode($this->httpGet($url));
            // $this->log($res, LogLevel::INFO);
            if (! empty($res->ticket)) {
                $ticket = $res->ticket;
                Cache::write($key, $ticket, $this->cache_data_config);
            }
            // 记录获取token的真实url
            $protocol = (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
            $url_acc = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
            // $this->log($url_acc, LogLevel::INFO);
        }
        // $this->log($ticket, LogLevel::INFO);
        return $ticket;
    }

    /**
     * 临时数据缓存
     * +1 hours
     */
    public $cache_data_config = "_memcache_1_hours_";

    public function getAccessToken($isClean = false)
    {
        $key = $this->appId . "_access_token_2";
        if ($isClean) {
            Cache::delete($key, $this->cache_data_config);
        }
        $access_token = Cache::read($key, $this->cache_data_config);
        if (empty($access_token)) {
            // 如果是企业号用以下URL获取access_token
            // $url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=$this->appId&corpsecret=$this->appSecret";
            $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$this->appId&secret=$this->appSecret";
            $res = json_decode($this->httpGet($url));
            if (! empty($res->access_token)) {
                $access_token = $res->access_token;
                Cache::write($key, $access_token, $this->cache_data_config);
            }
        }
        // $this->log($access_token, LogLevel::INFO);
        return $access_token;
    }

    private function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i ++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 获取token
     */
    public function getWxAccessToken($code)
    {
        $url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" . $this->appId . "&secret=" . $this->appSecret . "&code=" . $code . "&grant_type=authorization_code";
        $data = json_decode($this->httpGet($url), true);
        return $data;
    }

    /**
     * 获取用户信息
     */
    public function getWxUserInfo($accesstoken, $openid)
    {
        $url = "https://api.weixin.qq.com/sns/userinfo?access_token=" . $accesstoken . "&openid=" . $openid . "&lang=zh_CN";
        $data = json_decode($this->httpGet($url), true);
        $data["privilege"] = ! empty($data["privilege"]) ? implode(",", $data["privilege"]) : "";
        return $data;
    }

    /**
     * 新增微信登录信息
     */
    public function saveWeixinUser($result, $code)
    {
        $MarketWeixinUsers = TableRegistry::get("User.MarketWeixinUsers");
        $result["code"] = $code;
        $entity = $this->findWxUserByOpenid($result["openid"]);
        if (empty($entity->id)) {
            $uinfo = $this->getWxUserInfo($result["access_token"], $result["openid"]);
            $this->log("获取高级信息", LogLevel::INFO);
            $this->log($uinfo, LogLevel::INFO);
            if (! empty($uinfo["errcode"]) && $uinfo["errcode"] == 40001) {
                $this->log($result, LogLevel::INFO);
                throw new NotFoundException("token失效");
            } else {
                $sex = $uinfo["sex"] - 1;
                if ($sex < 0) {
                    // 性别,1时是男性，值为2时是女性，值为0时是未知
                    $sex = 2;
                }
                $uinfo["appid"] = $this->appId;
                $uinfo["uid"] = 0;
            }
            // 自动注册木丰号
            // $controller = $this->getController();
            // $uinfo["uid"] = $this->Users->registWithWechat($result["openid"], $uinfo["nickname"], $sex, $controller->getSecret(), $controller->getIp(), $controller->getAreaByIp($controller->getIp()), $inviteUid);
            // $this->log("自动注册木丰号：" . $result["openid"] . " " . $uinfo["uid"], LogLevel::INFO);
            $entity = $MarketWeixinUsers->newEntity($uinfo);
        }
        $entity = $MarketWeixinUsers->patchEntity($entity, $result);
        if ($MarketWeixinUsers->save($entity)) {
            return $entity->uid;
        }
        return false;
    }

    /**
     * 根据openid 查询用户
     */
    public function findWxUserByOpenid($openid)
    {
        $MarketWeixinUsers = TableRegistry::get("User.MarketWeixinUsers");
        return $MarketWeixinUsers->find()
            ->where([
            'openid' => $openid
        ])
            ->first();
    }

    /**
     * 根据用户id，查询指定app下的openid
     *
     * @param unknown $uid            
     * @return string
     */
    public function findWxUserOpenidByUid($uid)
    {
        $MarketWeixinUsers = TableRegistry::get("User.MarketWeixinUsers");
        $usr = $MarketWeixinUsers->find()
            ->where([
            'uid' => $uid,
            'appid' => $this->appId
        ])
            ->select([
            'openid'
        ])
            ->first();
        return ! empty($usr) ? $usr->openid : "";
    }
    /**
     * 发送线下店铺审核通知
     * @param 申请id $applyId
     */
    public function sendStoreApplysMessage($storeApplyEntity){
        $temid="EK-vR5kTG1vDKtPp11joRrCjf80dFDYDzA7ZUM8BXMI";
        //查询审核信息，得到uid等相关资料
        $url="http://www.mofing.com";//审核失败-->修改地址;审核成功-->下载app
        //模板格式
        $data = array(
            "first" => "您的资料已经审核通过。",
            "keyword1" => $uinfo["name"],
            "keyword2" => $uinfo["tel"],
            "keyword3" => date("Y年m月d日 H:i"),
            "remark" => "欢迎成为特约审稿人，点此快速选择教辅书。"
        );
        return $this->_sendMessageByTemplate($uid, $temid, $url, $data);
    }

    private function _sendMessageByTemplate($uid, $temid, $url1, $sdata)
    {
        $openid = $this->findWxUserOpenidByUid($uid); // 根据uid获取openid
        if(empty($openid)){
            return false;
        }
        $access_token = $this->getAccessToken();
        $url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" . $access_token;
        $data = array(
            "touser" => $openid,
            "template_id" => $temid,
            "url" => $url1,
            "topcolor" => "#12B7F5",
            "data" => array()
        );
        $sdata2 = array();
        foreach ($sdata as $k => $v) {
            $sdata2[$k] = array(
                "value" => $v,
                "color" => "#333333"
            );
        }
        $data["data"] = $sdata2;
        $data_string = json_encode($data);
        return $this->httpPostJson($url, $data_string);
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see \Payment\Controller\Component\OmnipayComponent::getName()
     */
    public function getName()
    {
        return "wechat";
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see \Payment\Controller\Component\OmnipayComponent::getNotifyUrl()
     */
    public function getNotifyUrl()
    {
        // /payment/results/notify
        return Router::url([
            "controller" => "results",
            "plugin" => "payment",
            "action" => "notify",
            $this->getName(),
            $this->id
        ], true);
    }

    /**
     *
     * {@inheritdoc}
     *
     * @see \Payment\Controller\Component\OmnipayComponent::getReturnUrl()
     */
    public function getReturnUrl()
    {
        // /payment/results/notify
        return Router::url([
            "controller" => "results",
            "plugin" => "payment",
            "action" => "returns",
            $this->getName(),
            $this->id
        ], true);
    }

    private function httpGet($url)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 500);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_URL, $url);
        
        $res = curl_exec($curl);
        curl_close($curl);
        
        return $res;
    }

    private function httpPostJson($url, $json_string)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json_string);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'Content-Type: application/json; charset=utf-8',
            'Content-Length: ' . strlen($json_string)
        ));
        ob_start();
        curl_exec($ch);
        $return_content = ob_get_contents();
        ob_end_clean();
        // $return_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        return $return_content;
    }
}
